﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using l1cs.server.model.Instance;
using l1cs.server.templates;
using l1cs.server.utils;

namespace l1cs.server.datatables
{
    class NpcTable
    {
        static Logger _log = Logger.getLogger( typeof( NpcTable ).FullName );

        private static NpcTable _instance;

        private Dictionary<int, L1Npc> _npcs = new Dictionary<int, L1Npc>();

        private Dictionary<String, Type> _constructorCache = new Dictionary<String, Type>();

        public static NpcTable getInstance()
        {
            if ( _instance == null )
            {
                _instance = new NpcTable();
            }
            return _instance;
        }

        private NpcTable()
        {/*TTOK*/
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                // NPC家族ハッシュ作成
                Dictionary<String, int> _familyTypes = new Dictionary<String, int>();
                int id = 0;
                String sqlstr = "select distinct family from npc WHERE trim(family) <> ''";
                foreach ( DataRow item in wrapper.GetDataTable( sqlstr ).Rows )
                {
                    String family = Convert.ToString( item["family"] );
                    _familyTypes[family] = id++;
                }

                // NPCハッシュ作成
                sqlstr = "SELECT * FROM npc";
                foreach ( DataRow item in wrapper.GetDataTable( sqlstr ).Rows )
                {
                    L1Npc npc = new L1Npc();
                    int npcId = SqlUtils.toInt( item["npcid"] );
                    npc.set_npcId( npcId );
                    npc.set_name( Convert.ToString( item["name"] ) );
                    npc.set_nameid( Convert.ToString( item["nameid"] ) );
                    npc.setImpl( Convert.ToString( item["impl"] ) );
                    npc.set_gfxid( SqlUtils.toInt( item["gfxid"] ) );
                    npc.set_level( SqlUtils.toInt( item["lvl"] ) );
                    npc.set_hp( SqlUtils.toInt( item["hp"] ) );
                    npc.set_mp( SqlUtils.toInt( item["mp"] ) );
                    npc.set_ac( SqlUtils.toInt( item["ac"] ) );
                    npc.set_str( Convert.ToByte( item["str"] ) );
                    npc.set_con( Convert.ToByte( item["con"] ) );
                    npc.set_dex( Convert.ToByte( item["dex"] ) );
                    npc.set_wis( Convert.ToByte( item["wis"] ) );
                    npc.set_int( Convert.ToByte( item["intel"] ) );
                    npc.set_mr( SqlUtils.toInt( item["mr"] ) );
                    npc.set_exp( SqlUtils.toInt( item["exp"] ) );
                    npc.set_lawful( SqlUtils.toInt( item["lawful"] ) );
                    npc.set_size( Convert.ToString( item["size"] ) );
                    npc.set_weakAttr( SqlUtils.toInt( item["weakAttr"] ) );
                    npc.set_ranged( SqlUtils.toInt( item["ranged"] ) );
                    npc.setTamable( Convert.ToBoolean( item["tamable"] ) );
                    npc.set_passispeed( SqlUtils.toInt( item["passispeed"] ) );
                    npc.set_atkspeed( SqlUtils.toInt( item["atkspeed"] ) );
                    npc.setAltAtkSpeed( SqlUtils.toInt( item["alt_atk_speed"] ) );
                    npc.setAtkMagicSpeed( SqlUtils.toInt( item["atk_magic_speed"] ) );
                    npc.setSubMagicSpeed( SqlUtils.toInt( item["sub_magic_speed"] ) );
                    npc.set_undead( SqlUtils.toInt( item["undead"] ) );
                    npc.set_poisonatk( SqlUtils.toInt( item["poison_atk"] ) );
                    npc.set_paralysisatk( SqlUtils.toInt( item["paralysis_atk"] ) );
                    npc.set_agro( Convert.ToBoolean( item["agro"] ) );
                    npc.set_agrososc( Convert.ToBoolean( item["agrososc"] ) );
                    npc.set_agrocoi( Convert.ToBoolean( item["agrocoi"] ) );
                    String family = Convert.ToString( item["family"] );
                    if ( family == "" )
                    {
                        npc.set_family( 0 );
                    }
                    else
                    {
                        int familyid;
                        _familyTypes.TryGetValue( Convert.ToString( item["family"] ), out familyid );
                        npc.set_family( familyid );
                    }
                    int agrofamily = SqlUtils.toInt( item["agrofamily"] );
                    if ( npc.get_family() == 0 && agrofamily == 1 )
                    {
                        npc.set_agrofamily( 0 );
                    }
                    else
                    {
                        npc.set_agrofamily( agrofamily );
                    }
                    npc.set_agrogfxid1( SqlUtils.toInt( item["agrogfxid1"] ) );
                    npc.set_agrogfxid2( SqlUtils.toInt( item["agrogfxid2"] ) );
                    npc.set_picupitem( Convert.ToBoolean( item["picupitem"] ) );
                    npc.set_digestitem( SqlUtils.toInt( item["digestitem"] ) );
                    npc.set_bravespeed( Convert.ToBoolean( item["bravespeed"] ) );
                    npc.set_hprinterval( SqlUtils.toInt( item["hprinterval"] ) );
                    npc.set_hpr( SqlUtils.toInt( item["hpr"] ) );
                    npc.set_mprinterval( SqlUtils.toInt( item["mprinterval"] ) );
                    npc.set_mpr( SqlUtils.toInt( item["mpr"] ) );
                    npc.set_teleport( Convert.ToBoolean( item["teleport"] ) );
                    npc.set_randomlevel( SqlUtils.toInt( item["randomlevel"] ) );
                    npc.set_randomhp( SqlUtils.toInt( item["randomhp"] ) );
                    npc.set_randommp( SqlUtils.toInt( item["randommp"] ) );
                    npc.set_randomac( SqlUtils.toInt( item["randomac"] ) );
                    npc.set_randomexp( SqlUtils.toInt( item["randomexp"] ) );
                    npc.set_randomlawful( SqlUtils.toInt( item["randomlawful"] ) );
                    npc.set_damagereduction( SqlUtils.toInt( item["damage_reduction"] ) );
                    npc.set_hard( Convert.ToBoolean( item["hard"] ) );
                    npc.set_doppel( Convert.ToBoolean( item["doppel"] ) );
                    npc.set_IsTU( Convert.ToBoolean( item["IsTU"] ) );
                    npc.set_IsErase( Convert.ToBoolean( item["IsErase"] ) );
                    npc.setBowActId( SqlUtils.toInt( item["bowActId"] ) );
                    npc.setKarma( SqlUtils.toInt( item["karma"] ) );
                    npc.setTransformId( SqlUtils.toInt( item["transform_id"] ) );
                    npc.setTransformGfxId( SqlUtils.toInt( item["transform_gfxid"] ) );
                    npc.setLightSize( SqlUtils.toInt( item["light_size"] ) );
                    npc.setAmountFixed( Convert.ToBoolean( item["amount_fixed"] ) );
                    npc.setChangeHead( Convert.ToBoolean( item["change_head"] ) );
                    npc.setCantResurrect( Convert.ToBoolean( item["cant_resurrect"] ) );
                    _npcs[npcId] = npc;
                    // NPCクラス種類をハッシュする
                    String implName = npc.getImpl();
                    if ( implName != "" && !_constructorCache.ContainsKey( implName ) )
                    {
                        Type t = Type.GetType( "l1cs.server.model.Instance." + implName + "Instance" );
                        _constructorCache[implName] = t;
                    }
                }
            }
        }

        /// <summary>
        /// NpcidからL1Npcを返す。
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public L1Npc getTemplate( int id )
        {/*TTOK*/
            L1Npc ret;
            _npcs.TryGetValue( id, out ret );
            return ret;
        }

        public L1NpcInstance newNpcInstance( int id )
        {/*TTOK*/
            L1Npc npcTemp = getTemplate( id );
            if ( npcTemp == null )
            {
                throw new ArgumentException( String.Format( "NpcTemplate: {0} not found", id ) );
            }
            return newNpcInstance( npcTemp );
        }

        public L1NpcInstance newNpcInstance( L1Npc template )
        {/*TTOK*/
            Type t = _constructorCache[template.getImpl()];
            object obj = t.InvokeMember( null,
                BindingFlags.CreateInstance,
                null, null,
                new object[] { template } );
            return obj as L1NpcInstance;
        }

        public int findNpcIdByName( String name )
        {/*TTOK*/
            foreach ( L1Npc npc in _npcs.Values )
            {
                if ( npc.get_name().Equals( name ) )
                {
                    return npc.get_npcId();
                }
            }
            return 0;
        }

        public int findNpcIdByNameWithoutSpace( String name )
        {/*TTOK*/
            foreach ( L1Npc npc in _npcs.Values )
            {
                if ( npc.get_name().Replace( " ", "" ).Equals( name ) )
                {
                    return npc.get_npcId();
                }
            }
            return 0;
        }
    }
}
